package chagine.mdt.utils;

import chagine.mdt.Constants;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.time.LocalDate;
import java.time.LocalDateTime;

public class ExpectedUtil {

    public static void main(String[] args) {
        GestationalWeek week = calculationGestationalWeek(GestationalWeekType.LAST_MENSTRUAL_PERIOD, LocalDate.of(2022,5,1));
        System.out.println("week = " + week);
    }

    /**
     * 计算孕周
     *
     * @param type      计算类型
     * @param localDate 末次月经/预产期
     */
    public static GestationalWeek calculationGestationalWeek(GestationalWeekType type, LocalDate localDate) {
        if (GestationalWeekType.LAST_MENSTRUAL_PERIOD == type || GestationalWeekType.EXPECTED_DATE == type) {
            return calculationGestationalWeek(type, localDate, null, null);
        }
        return calculationGestationalWeek(type, localDate);
    }

    /**
     * 计算孕周
     *
     * @param type          计算类型
     * @param localDate     末次月经/预产期
     * @param weekNumber    超声检查的孕周
     * @param weekDayNumber 超声检查的孕周天数
     */
    public static GestationalWeek calculationGestationalWeek(GestationalWeekType type, LocalDate localDate, Integer weekNumber, Integer weekDayNumber) {
        GestationalWeek outVo = null;
        Integer days = null;
        if (type == GestationalWeekType.LAST_MENSTRUAL_PERIOD && localDate != null) {
            LocalDate nowDate = LocalDate.now();
            days = (int) (nowDate.toEpochDay() - localDate.toEpochDay());
        } else if (type == GestationalWeekType.EXPECTED_DATE && localDate != null) {
            // 预产期
            LocalDate lastMenstrualDate = localDate.minusDays(280);
            days = (int) (LocalDate.now().toEpochDay() - lastMenstrualDate.toEpochDay());
        } else if (type == GestationalWeekType.ULTRASONIC_WEEK && localDate != null && weekNumber != null && weekDayNumber != null) {
            // 超声检查日期
            LocalDate lastMenstrualDate = localDate.minusWeeks(weekNumber).minusDays(weekDayNumber);
            days = (int) (LocalDate.now().toEpochDay() - lastMenstrualDate.toEpochDay());
        }
        if (days != null) {
            outVo = new GestationalWeek();
            outVo.setAllDay(days);
            outVo.setWeek(days / 7);
            outVo.setWeekDay(days % 7);
        }
        return outVo;
    }

    /**
     * 计算怀孕日期
     * @param data 孕期相关时间
     * @return
     */
    public static LocalDate calculationPregnancyDate(GestationalTimeData data) {
        LocalDateTime expectedDate = data.getExpectedDate();
        Integer lastMenstrualPeriodStatus = data.getLastMenstrualPeriodStatus();
        if (lastMenstrualPeriodStatus == null) {
            lastMenstrualPeriodStatus = 0;
        }
        LocalDateTime lastMenstrualPeriodDate = data.getLastMenstrualPeriodDate();
        LocalDateTime ultrasoundDate = data.getUltrasoundDate();
        Integer ultrasoundPregnancyWeek = data.getUltrasoundPregnancyWeek();
        Integer ultrasoundPregnancyDay = data.getUltrasoundPregnancyDay();
        if (expectedDate != null) {
            return expectedDate.toLocalDate().minusDays(280);
        } else if (lastMenstrualPeriodStatus == Constants.COMMON_YES && lastMenstrualPeriodDate != null) {
            return lastMenstrualPeriodDate.toLocalDate();
        } else if (ultrasoundDate != null && ultrasoundPregnancyWeek != null && ultrasoundPregnancyDay != null) {
            return ultrasoundDate.toLocalDate().minusWeeks(ultrasoundPregnancyWeek).minusDays(ultrasoundPregnancyDay);
        }

        return null;
    }

    /**
     * 计算孕周
     * @param pregnancyDate 怀孕日期
     * @return
     */
    public static GestationalWeek calculationGestational(LocalDate pregnancyDate) {
        if (pregnancyDate == null) {
            return GestationalWeek.builder().build();
        }
        int days = (int) (LocalDate.now().toEpochDay() - pregnancyDate.toEpochDay());

        return GestationalWeek.builder().allDay(days).week(days / 7).weekDay(days % 7).build();
    }

    public static LocalDate calculationExpectedDateByPregnancyDate(LocalDate pregnancyDate) {
        if (pregnancyDate == null) {
            return null;
        }
        return pregnancyDate.plusDays(280);
    }

    public static GestationalWeek calculationGestational(GestationalTimeData data) {
        return calculationGestational(calculationPregnancyDate(data));
    }

    /**
     * 孕周工具
     */
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class GestationalWeek {
        private Integer week;
        private Integer weekDay;
        private Integer allDay;
    }

    /**
     * 孕周计算类型，280天
     */
    public enum GestationalWeekType {

        /**
         * 末次月经
         */
        LAST_MENSTRUAL_PERIOD(1),

        /**
         * 预产期
         */
        EXPECTED_DATE(2),

        /**
         * 超声孕周
         */
        ULTRASONIC_WEEK(3);

        public final int code;

        GestationalWeekType(int code) {
            this.code = code;
        }
    }

    /**
     * 孕期相关的时间数据
     */
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @Builder
    public static class GestationalTimeData {
        /**
         * 末次月经，0-不确定 1-确定
         */
        private Integer lastMenstrualPeriodStatus;

        /**
         * 末次月经，日期
         */
        private LocalDateTime lastMenstrualPeriodDate;

        /**
         * 超声检查日期
         */
        private LocalDateTime ultrasoundDate;

        /**
         * 超声检查，孕期周数，周
         */
        private Integer ultrasoundPregnancyWeek;

        /**
         * 超声检查，孕期周数，天
         */
        private Integer ultrasoundPregnancyDay;

        /**
         * 最终预产期，日期，时间戳
         */
        private LocalDateTime expectedDate;
    }

}
